package com.spring.config;

import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.google.common.collect.Maps;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;

import javax.annotation.Nullable;
import java.lang.reflect.ParameterizedType;
import java.sql.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @Author zhangtao
 * @create 2024/2/26 10:59
 */
@MappedTypes(List.class)
public class DynamicJsonArrayTypeHandler extends BaseTypeHandler<List<?>> {
    /**
     * mp扫描到的表对应实体类中List及其子类类型属性的泛型信息
     */
    private static final Map<String, Map<String, Class<?>>> LIST_TYPE_FIELD_GENERIC_METADATA = Maps.newConcurrentMap();

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, List<?> parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter == null ? null : JSONArray.toJSONString(parameter));
    }

    @Override
    public List<?> getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return asList(rs.getMetaData(), rs.findColumn(columnName), rs.getString(columnName));
    }

    @Override
    public List<?> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return asList(rs.getMetaData(), columnIndex, rs.getString(columnIndex));
    }

    @Override
    public List<?> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return asList(cs.getMetaData(), columnIndex, cs.getString(columnIndex));
    }

    @Nullable
    private static List<?> asList(ResultSetMetaData resultSetMetaData, int columnIndex, String value) throws SQLException {
        // 必须指定类型, 否则会造成堆污染, 抛出java.lang.ClassCastException
        return value == null ? null : JSONArray.parseArray(value, findGenericRawType(resultSetMetaData, columnIndex));
    }

    private static Class<?> findGenericRawType(ResultSetMetaData resultSetMetaData, int columnIndex) throws SQLException {
        return LIST_TYPE_FIELD_GENERIC_METADATA.computeIfAbsent(
                resultSetMetaData.getTableName(columnIndex),
                k -> Optional.ofNullable(TableInfoHelper.getTableInfo(k))
                        .orElseThrow(() -> new IllegalArgumentException("对应表名不存在"))
                        // 读取表字段信息
                        .getFieldList().stream()
                        // 筛选指定泛型类型的List及其子类
                        .filter(f -> List.class.isAssignableFrom(f.getField().getType()))
                        .filter(f -> f.getField().getGenericType() instanceof ParameterizedType)
                        // key: 原始字段名, value: List类型字段指定的泛型类
                        .collect(Collectors.toMap(
                                TableFieldInfo::getColumn,
                                f -> Optional.ofNullable(((ParameterizedType) f.getField().getGenericType()).getActualTypeArguments()[0])
                                        // 如果是嵌套泛型类型则读取其原始类型
                                        .map(
                                                t -> t instanceof ParameterizedType
                                                        ? (Class<?>) ((ParameterizedType) t).getRawType()
                                                        : (Class<?>) t
                                        )
                                        .orElse(Object.class),
                                // 同名属性取第一个
                                (v1, v2) -> v1,
                                Maps::newConcurrentMap
                        ))
        ).getOrDefault(resultSetMetaData.getColumnName(columnIndex), Object.class);
    }
}
